package com.cc8w.shiro;


import com.cc8w.entity.UserActivePojo;
import com.cc8w.entity.UserPojo;
import com.cc8w.service.PermssionService;
import com.cc8w.service.RoleService;
import com.cc8w.service.UserService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;


/**
 * Shiro默认使用自带的IniRealm，IniRealm从ini配置文件中读取用户的信息，大部分情况下需要从系统的数据库中读取用户信息，所以需要自定义realm。
 * 最基础的是Realm接口，CachingRealm负责缓存处理，AuthenticationRealm负责认证，AuthorizingRealm负责授权，通常自定义的realm继承AuthorizingRealm
 */
@Component
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermssionService permssionService;
    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }



    /*
     * 登录信息和用户验证信息验证(non-Javadoc)
     * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.从authenticationToken中获取身份信息,其实就是用户的登录名
        String username = authenticationToken.getPrincipal().toString();
        String password = authenticationToken.getCredentials().toString();

        //2.根据用户名查询用户是否存在
        UserPojo user=userService.queryUserByUserName(username);
        System.out.println(user);
        //返回null说明用户不存在
        if(null!=user) {
            //2.1根据用户名去查询用户拥有哪些角色
            List<String> roles= roleService.queryRolesByUserName(user.getUserName());
            System.out.println(roles);
            //2.2根据用户名查询用户拥有哪些权限
            List<String> permissions=permssionService.queryPermissionsByUserName(user.getUserName());

            UserActivePojo activeUser=new UserActivePojo(user, roles, permissions);

           //3.返回认证信息(第一个不支持散列加密,第二个可以散列加密)
            /**
             * 参数1  用户身份
             * 参数2 用户在数据库里面存放的密码
             * 参数3 当前类名
             */
            //SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName());

            /**
             * 参数1 传到doGetAuthorizationInfo里面的getPrimaryPrincipal()对象或subject.getPrincipal()-我们封装的实体activeUser
             * 参数2 hashedCredentials加密后的密码(数据库查询的密码)-user.getPassword()
             * 参数3 混淆(盐)
             * 参数4 当前类名
             */
            //SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName)
            SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName());
            return info;

        }
        return null;
    }

    /*
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc)
     * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1.获得用户身份信息(PrincipalCollection有认证回调传来的第一个参数[activeUser])
        UserActivePojo activeUser = (UserActivePojo) principalCollection.getPrimaryPrincipal();
        System.out.println("doGetAuthorizationInfo");
        //2.根据身份信息获取权限数据
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();

        //3.根据用户查询用户的角色 (其实认证方法一并查询出来了,保存在UserActivePojo)
        List<String> roles = activeUser.getRoles();
        if(null!=roles&&roles.size()>0) {
            info.addRoles(roles);//添加角色
        }
        //4.根据用户查询用户的权限
        List<String> permissions=activeUser.getPermissions();
        if(null!=permissions&&permissions.size()>0) {
            info.addStringPermissions(permissions);//添加权限
        }

        /**
         * 总结:本来授权->查角色和权限都在本方法写(但是前端每查询一次权限,就会回调本方法一次,
         * 所以直接查数据库,对数据库有压力),所以最后,
         * 1.把查角色和权限方法写在了认证,然后封装成activeUser传递过来,这样Controller每次查权限,就不用查数据库了,直接在activeUser获取即可.
         * 2.缓存应该也可以解决
         */


        return info;
    }


}
